﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Castle.ActiveRecord;
using Castle.ActiveRecord.Queries;
using NHibernate.Criterion;

namespace Matriculex.Dominio
{
    [ActiveRecord("periodo_rematricula")]
    public class PeriodoRematricula : RemovableEntityWithId<PeriodoRematricula>
    {
        // construtor vazio necessário para o ActiveRecord
        protected PeriodoRematricula() { }
        public PeriodoRematricula(Ciclo ciclo, DateTime inicio, DateTime fim)
        {
            this.Inicio = inicio;
            this.Ciclo = ciclo;
            this.Fim = fim;
            this.Oferecimentos = new List<Oferecimento>();
        }

        public bool CausaIntereseccao()
        {
            //deve-se verificar se todos os períodos Pi existentes tem sua data de início
            // superior a data de fim do novo período ou tem sua data de fim inferior
            // a data de início do novo período (da especificação)
            PeriodoRematricula[] periodos = PeriodoRematricula.FindAll();
            foreach (var p in periodos)
            {
                if (p == this) continue;
                if (!(DateTime.Compare(p.Inicio.Value, this.Fim.Value) > 0
                    || DateTime.Compare(p.Fim.Value, this.Inicio.Value) < 0))
                    return true;
            }
            return false;
        }

        public override string ToString()
        {
            return String.Format("{0}: {1:dd/MM/yyyy} a {2:dd/MM/yyyy}", Ciclo, Inicio, Fim);
        }

        public override void Save()
        {
            if (CausaIntereseccao())
                throw new DominioException("Já existe um período de rematrícula nesse intervalo.");

            base.Save();
        }

        /// <summary>
        /// Se não houver nenhum aluno cadastrado, remove o período de rematrícula e todos
        /// os oferecimentos
        /// </summary>
        public override void Delete()
        {
            // vê se pode remover
            foreach (var oferecimento in Oferecimentos)
            {
                if (oferecimento.Alunos.Count > 0)
                {
                    throw new DominioException("Não é possível remover um período de rematrícula ao qual há alunos matriculados.");
                }
            }
            // e remove 'de verdade'
            foreach (var oferecimento in Oferecimentos)
            {
                oferecimento.Delete();
            }
            base.Delete();
        }

        [Property]
        public DateTime? Inicio { get; set; }
        [Property]
        public DateTime? Fim { get; set; }
        [Nested]
        public Ciclo Ciclo { get; set; }
        private IList<Oferecimento> oferecimentos;
        [HasMany(Inverse=true, Cascade=ManyRelationCascadeEnum.All)]
        public IList<Oferecimento> Oferecimentos
        {
            get
            {
                return new List<Oferecimento>(oferecimentos).AsReadOnly();
            }
            private set { oferecimentos = value; }
        }


        public static PeriodoRematricula[] FindAllDescByCiclo()
        {
            return FindAll(new[] { Order.Desc("Ciclo.Ano"), Order.Desc("Ciclo.Trimestre")});
        }

        public static PeriodoRematricula GetPeriodoVigente()
        {
            var query = new SimpleQuery<PeriodoRematricula>(
                @"from PeriodoRematricula p where p.Inicio <= :now and :now <= p.Fim");
            query.SetParameter("now", DateTime.Now);
            var r = query.Execute().Where(p => p.Ativado);
            return r.FirstOrDefault();
        }

        public static PeriodoRematricula GetPeriodoByCiclo(Ciclo ciclo)
        {
            var query = new SimpleQuery<PeriodoRematricula>(
                @"from PeriodoRematricula p where p.Ciclo.Ano = :ano and p.Ciclo.Trimestre = :trimestre");
            query.SetParameter("ano", ciclo.Ano);
            query.SetParameter("trimestre", ciclo.Trimestre);
            var r = query.Execute().Where(p => p.Ativado);
            return r.FirstOrDefault();
        }
    }
}
